const PROMISE_STATUS = {
  PENDING: 'PENDING',
  RESOLVED: 'RESOLVED',
  REJECTED: 'REJECTED',
}

const PROMISE_HANDLER_TYPE = {
  THEN: 'THEN',
  CATCH: 'CATCH',
  FINALLY: 'FINALLY'
}

class MyPromise {
  constructor(handler) {
    if (typeof handler !== 'function') {
      throw Error('then argument must be Function')
    }
    this.status = PROMISE_STATUS.PENDING
    this.value = undefined
    // setTimeout(() => {
      try {
        handler(this.resolve.bind(this), this.reject.bind(this))
      } catch (error) {
        this.reject(error)
      }
    // }, 0)
  }
  resolve(result) {
    if (this.status !== PROMISE_STATUS.PENDING) {
      return
    }
    this.status = PROMISE_STATUS.RESOLVED
    this.value = result
    if (this.onResolve) {
      this.runFunc(this.onResolve)
    }
  }
  reject(result) {
    if (this.status !== PROMISE_STATUS.PENDING) {
      return
    }
    this.status = PROMISE_STATUS.REJECTED
    this.value = result
    if (this.onReject) {
      this.runFunc(this.onReject)
    }
  }
  runFunc(func, val = this.value, resolve = MyPromise.resolve, reject = MyPromise.reject) {
    setTimeout(() => {
      try {
        const result = func(val)
        if (result instanceof MyPromise) {
          return result.then(resolve, reject)
        } else {
          return resolve(result)
        }
      } catch(err) {
        return reject(err)
      }
    }, 0)
  }
  getFunc(resolve, reject, func) {
    return val => this.runFunc(func, val, resolve, reject)
  }
  then(onResolve = val => val, onReject = err => { throw err }) {
    if (this.status !== PROMISE_STATUS.PENDING) {
      const func = this.status === PROMISE_STATUS.RESOLVED? onResolve: onReject
      return this.runFunc(func)
    } else {
      const nextP = new MyPromise((resolve, reject) => {
        this.onResolve = this.getFunc(resolve, reject, onResolve)
        this.onReject = this.getFunc(resolve, reject, onReject)
      })
      return nextP
    }
  }
  catch(onReject = err => { throw err }) {
    return this.then(undefined, onReject)
  }
  finally(onFinally) {
    return this.then(
      val  => MyPromise.resolve(onFinally()).then(() => val),
      err => MyPromise.resolve(onFinally()).then(() => { throw err })
    )
  }
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = []
      promises.forEach(p => {
        p.then(val => {
          results.push(val)
          if (results.length === promises.length) {
            resolve(results)
          }
        }).catch(reject)
      })
    })
  }
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      const results = []
      let isCallback = false
      promises.forEach(p => {
        p.then(val => {
          if (!isCallback) {
            isCallback = true
            resolve(val)
          }
        }).catch(reject)
      })
    })
  }
  static reject(result) {
    return new MyPromise((resolve, reject) => {
      reject(result)
    })
  }
  static resolve(result) {
    return new MyPromise((resolve, reject) => {
      resolve(result)
    })
  }
}
// .finally(val => {
//   console.log('finally', val)
//   return 2
// })
// const p = new MyPromise((resolve, reject) => {
//   resolve(1)
// })
// const p2 = p.then(val => {
//   console.log('then', val)
//   return new MyPromise((resolve, reject) => {
//     setTimeout(() => {
//       console.log(1)
//       resolve(++val)
//     }, 300)
//   })
// })
// console.log(2)
// const p3 = p2.then(val => {
//   console.log('then 2', val)
//   return ++val
// }).then(val => {
//   console.log('then 3', val)
//   return ++val
// }).catch(err => {
//   console.log('err 2', err)
//   return err + 2
// })
// console.log(p, p2, p3)
new MyPromise((r, r2) => {
  const time  = Date.now()
  while(Date.now() - time < 1000) {
  }
  console.log(1)
  r()
}).then(val => {
  const time  = Date.now()
  while(Date.now() - time < 1000) {
  }
  console.log(2)
})
console.log(3)
console.log(5)